#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>

#define DRV_NAME "pdc42819"
#define FASTTRAK_DEV_NAME "pdc42819"
#define FASTTRAK_DRIVER_DATE	"(in future)"
#define PDC42819_DRIVER_VERSION "1.0.0000.1"
#define ST_VER_MAJOR 		1
#define ST_VER_MINOR 		0
#define ST_OEM 			0
#define ST_BUILD_VER 		1

//Device define 
#define MAX_ARRAY_SUPPORTED 4 /*I don`t know... 4 for test.*/
#define PDC_MAX_TARGET_NUM	(MAX_ARRAY_SUPPORTED+1)
#define PDC_MAX_LUN_PER_TARGET		1
#define CAN_QUEUE					32
#define FASTTRAK_MAX_SG 				24	/* for ASIC workaround, MAX_SG < 32, 20070531  */
#define MAX_SG 							FASTTRAK_MAX_SG
#define MAX_TIMER_QUEUE 				32
#define MAX_ADAPTORS	 5


MODULE_AUTHOR("stalkerg");
MODULE_DESCRIPTION("Promise Technology SuperTrak TX Controllers");
MODULE_LICENSE("GPL");
MODULE_VERSION(PDC42819_DRIVER_VERSION);

/* global var definitions */
static struct proc_dir_entry *proc_scsi_pdc42819;
unsigned char * pdc42819_hostp[MAX_ADAPTORS]={0};


struct pdc_hba {
	void __iomem *mmio_base;	/* iomapped PCI memory space */
	void *dma_mem;
	dma_addr_t dma_handle;
	size_t dma_size;

	struct Scsi_Host *host;
	struct pci_dev *pdev;

	u32 req_head;
	u32 req_tail;
	u32 status_head;
	u32 status_tail;

	struct status_msg *status_buffer;
	void *copy_buffer; /* temp buffer for driver-handled commands */

	unsigned int mu_status;
	int out_req_cnt;

	unsigned int cardtype;
};

//prtotype
static int pdc42819_biosparam( struct scsi_device *sd, struct block_device *bd, sector_t t, int Geometry[]) {
	printk("pdc42819_biosparam\n");
	/*if (disk->capacity > 0x200000) {	// 1GB
  		Geometry[0]=255;	// heads
  		Geometry[1]=63;  	// sectors
  		Geometry[2]=disk->capacity/(Geometry[0]*Geometry[1]); // cylinders

		// capacity larger than 526GB(C/H/S = 65535/255/63) commented by isaac for fdisk issue
  		//if (Geometry[2] > 65535) {
   	   	//	Geometry[2] = 65535;
   	   	//	Geometry[1] = disk->capacity / (Geometry[2]*Geometry[0]);
  		//} 
   	}else{
   		Geometry[0]=64;  		
   		Geometry[1]=32;  		
   		Geometry[2]=disk->capacity / (Geometry[0]*Geometry[1]);  
   	}
	printk("pdc42819_biosparam capacity = 0x%x, Heads = 0x%x, Sectors = 0x%x, Cylinders = 0x%x\n", disk->capacity, Geometry[0], Geometry[1], Geometry[2]);
*/   	return 0;
}

//prtotype
static int pdc42819_queue(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *)) {
	printk("pdc42819_queue\n");
	return 0;
}

static int pdc42819_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
			int length, int func) {
	printk("pdc42819_proc_info\n");
	int len;

	*start = buffer;
	if (func == 1) {
		/*
		 * The user has done a write and wants us to take the
		 * data in the buffer and do something with it.
		 * proc_scsiwrite calls us with inout = 1
		 *
		 * Read data from buffer (writing to us) - NOT SUPPORTED
		 */
		return -EINVAL;
	}

	len = sprintf(buffer,"***** T3 Driver %s %s*****\n", PDC42819_DRIVER_VERSION, FASTTRAK_DRIVER_DATE);

	len += sprintf(buffer + len,"***** Copyright 2004 by Promise Technology, Inc.   *****\n\n");

	*(buffer + len) = '\0';
 
	*start = buffer + (offset);	/* Start of wanted data */
	len -= (offset );
	if(len > length) {
		len = length;
	} else if(len < 0){
		len = 0;
		**start = '\0';
	}
	return len;
}

//prtotype
static int pdc42819_abort(struct scsi_cmnd *cmd) {
	printk("pdc42819_abort: returning SCSI_ABORT_ERROR\n");

   	return SUCCESS;
}

//prtotype
static int pdc42819_reset(struct scsi_cmnd *cmd) {
	printk("pdc42819_reset\n");
	return SUCCESS;
}

static struct scsi_host_template driver_template = {
	.module	=THIS_MODULE,
	.name = FASTTRAK_DEV_NAME,
	.queuecommand = pdc42819_queue,
	.eh_abort_handler = pdc42819_abort,
	.eh_device_reset_handler = pdc42819_reset,
	.eh_host_reset_handler = pdc42819_reset,
	.bios_param = pdc42819_biosparam,
	/*.can_queue = 1,*/
	.can_queue = CAN_QUEUE-2,
	.this_id = -1,
	.sg_tablesize = MAX_SG,
	.cmd_per_lun = CAN_QUEUE-2,
	.use_clustering = DISABLE_CLUSTERING,
#ifndef  _OBSOLETE_PROC_	
	.proc_dir = &proc_scsi_pdc42819,                                               
    	.proc_info = pdc42819_proc_info, 
#endif /*  _OBSOLETE_PROC_ */
	.proc_name = FASTTRAK_DEV_NAME,
};

int fasttrak_set_dma_mask(struct pci_dev * pdev)
{
	int rc;

	/* try to set the DMA mask to 64bit */
	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
		rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
		if (rc) {
			rc = pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(32));
			if (rc) {
				dev_printk(KERN_ERR, &pdev->dev,
					   "64-bit DMA enable failed\n");
				return rc;
			}
		}
	} else {
		/* we couldn't set the DMA mask to 64bit, let's try 32bit */
		rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
		if (rc) {
			dev_printk(KERN_ERR, &pdev->dev,
				   "32-bit DMA enable failed\n");
			return rc;
		}
		rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
		if (rc) {
			dev_printk(KERN_ERR, &pdev->dev,
				   "32-bit consistent DMA enable failed\n");
			return rc;
		}
	}
	return 0;
}

static void pdc42819_enable_intx(struct pci_dev *pdev)
{
        u16 pci_command;

        pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
        if (pci_command & PCI_COMMAND_INTX_DISABLE) {
                printk("pdc42819_enable_intx So it is INTX/MSI?\n");
                pci_command &= ~PCI_COMMAND_INTX_DISABLE;
                pci_write_config_word(pdev, PCI_COMMAND, pci_command);
        }
}


//prtotype
static int __devinit
pdc42819_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	printk("pdc42819_probe start\n");
	struct Scsi_Host *host;
	struct pdc_hba *hba;
	u32 rc = PCIBIOS_SUCCESSFUL;
	
	/*Step1: enable and initialize pci device*/
        rc = pci_enable_device(pdev);
	if (rc) {
		dev_err(&pdev->dev, "Cannot enable this device.\n");
		return rc;
	}
	printk("pdc42819_probe request_regions.\n");
	rc = pci_request_regions(pdev, FASTTRAK_DEV_NAME);
	if (rc)
		goto err_out;
        pdc42819_enable_intx(pdev);
	
	printk("pdc42819_probe set dma mask.\n");
	rc = fasttrak_set_dma_mask(pdev);
	if (rc)
		goto err_out_regions;

	pci_set_master(pdev);

	dev_info(&pdev->dev, "Found pdc42819 Controller %d:%d with IRQ: %d\n", id->vendor, id->device, pdev->irq);
	dev_info(&pdev->dev, "Driver version of pdc42819 : %s\n", PDC42819_DRIVER_VERSION);

	/*Step2: alloc and initialize scsi host */
	host = scsi_host_alloc(&driver_template, sizeof(struct pdc_hba));
	if (!host) {
		dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n");
		rc = -ENOMEM;
		goto err_out_regions;
	}
	hba = (struct pdc_hba *)host->hostdata;
	memset(hba, 0, sizeof(struct pdc_hba));
	hba->host = host;
	hba->pdev = pdev;

	host->max_channel = 0;
    	host->max_id = PDC_MAX_TARGET_NUM;
    	host->max_lun = PDC_MAX_LUN_PER_TARGET;	
	// jason
	host->max_cmd_len = MAX_COMMAND_SIZE;

	hba->mmio_base = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
	if ( !hba->mmio_base) {
		printk(KERN_ERR DRV_NAME "(%s): memory map failed\n",
			pci_name(pdev));
		rc = -ENOMEM;
		goto err_out_regions;
	}
	printk("pdc42819_probe Start scan scsi host.");
	scsi_scan_host(host);
	return 0;
//Errors out
err_out_pci_free:
	dma_free_coherent(&pdev->dev, hba->dma_size,
			  hba->dma_mem, hba->dma_handle);
err_out_iounmap:
	iounmap(hba->mmio_base);
err_out_host:
	scsi_host_put(host);
err_out_regions:
	pci_release_regions(pdev);
err_out:
	pci_disable_device(pdev);

	return rc;
}


static void pdc42819_hba_stop(struct pdc_hba *hba)
{
	printk("pdc42819_hba_stop\n");
}

static void pdc42819_hba_free(struct pdc_hba *hba)
{
	printk("pdc42819_hba_free\n");
	free_irq(hba->pdev->irq, hba);

	iounmap(hba->mmio_base);

	pci_release_regions(hba->pdev);

	dma_free_coherent(&hba->pdev->dev, hba->dma_size,
			  hba->dma_mem, hba->dma_handle);
}

static void pdc42819_remove(struct pci_dev *pdev)
{
	printk("pdc42819_remove\n");
	struct pdc_hba *hba = pci_get_drvdata(pdev);
	
	pci_release_regions(pdev);
	pci_disable_device(pdev);
	//scsi_remove_host(hba->host);
	//pci_set_drvdata(pdev, NULL);
	
	//pdc42819_hba_stop(hba);
	//pdc42819_hba_free(hba);
	
	//scsi_host_put(hba->host);
	//
}

static void pdc42819_shutdown(struct pci_dev *pdev)
{
	printk("pdc42819_shutdown\n");
	struct pdc_hba *hba = pci_get_drvdata(pdev);
	//pdc42819_hba_stop(hba);
}

static struct pci_device_id pdc42819_pci_tbl[] = {
	{ 0x105a, 0x3f20,  PCI_ANY_ID, PCI_ANY_ID, 0, 0 },   /* PDC42819 */
	{ }	/* terminate list */
};

MODULE_DEVICE_TABLE(pci, pdc42819_pci_tbl);

static struct pci_driver pdc42819_pci_driver = {
	.name		= DRV_NAME,
	.id_table	= pdc42819_pci_tbl,
	.probe		= pdc42819_probe,
	.remove		= __devexit_p(pdc42819_remove),
	.shutdown	= pdc42819_shutdown,
};

static int __init pdc42819_init(void)
{
	printk(KERN_INFO DRV_NAME
		": Promise FastTrak Driver version: %s\n",
		 PDC42819_DRIVER_VERSION);

	return pci_register_driver(&pdc42819_pci_driver);
}

static void __exit pdc42819_exit(void)
{
	printk("pdc42819_exit\n");
	pci_unregister_driver(&pdc42819_pci_driver);
}

module_init(pdc42819_init);
module_exit(pdc42819_exit);
